﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.Web.Mvc;
using PaymentHelper.Core.Extension;
using PaymentHelper.Core.RelatedObject;
using PaymentHelper;
using PaymentHelper.Methods.PaypalDirect.com.paypalobjects.www;
using PaymentHelper.Core.CommonUtils;

namespace PaymentHelper.Methods.PaypalDirect
{
    public class PaypalDirectProcessor : BaseExtension, IPaymentMethod
    {
        public bool SupportCapture
        {
            get { return true; }
        }

        public bool SupportRefund
        {
            get { return true; }
        }

        public bool SupportVoid
        {
            get { return true; }
        }

        public PaymentMethodType PaymentMethodType
        {
            get
            {
                return PaymentMethodType.Direction;
            }
            set
            {
                PaymentMethodType = value;
            }
        }

        public void ProcessPayment(ProcessPaymentRequest processPaymentRequest)
        {
            var result = new ProcessPaymentResult();

            var req = new DoDirectPaymentReq();
            req.DoDirectPaymentRequest = new DoDirectPaymentRequestType();
            req.DoDirectPaymentRequest.Version = "63";

            var details = new DoDirectPaymentRequestDetailsType();
            details.PaymentAction = PaymentActionCodeType.Sale;

            //credit card
            details.CreditCard = new CreditCardDetailsType();
            details.CreditCard.CreditCardNumber = processPaymentRequest.Order.CreditCard.CreditCardNumber;
            details.CreditCard.CreditCardType = (CreditCardTypeType)Enum.Parse(typeof(CreditCardTypeType), processPaymentRequest.Order.CreditCard.CreditCardType);
            details.CreditCard.ExpMonthSpecified = true;
            details.CreditCard.ExpMonth = processPaymentRequest.Order.CreditCard.CreditCardExpireMonth;
            details.CreditCard.ExpYearSpecified = true;
            details.CreditCard.ExpYear = processPaymentRequest.Order.CreditCard.CreditCardExpireYear;
            details.CreditCard.CVV2 = processPaymentRequest.Order.CreditCard.CreditCardCvv2;
            details.CreditCard.CardOwner = new PayerInfoType();

            CountryCodeType country;
            Enum.TryParse<CountryCodeType>(processPaymentRequest.Order.BillingAddress.Country, out country);
            details.CreditCard.CardOwner.PayerCountry = country;
            details.CreditCard.CreditCardTypeSpecified = true;

            //billing address
            details.CreditCard.CardOwner.Address = new AddressType();
            details.CreditCard.CardOwner.Address.CountrySpecified = true;
            details.CreditCard.CardOwner.Address.Street1 = processPaymentRequest.Order.BillingAddress.Address1;
            details.CreditCard.CardOwner.Address.Street2 = processPaymentRequest.Order.BillingAddress.Address2;
            details.CreditCard.CardOwner.Address.CityName = processPaymentRequest.Order.BillingAddress.City;

            details.CreditCard.CardOwner.Address.StateOrProvince = "";
            details.CreditCard.CardOwner.Address.Country = country;
            details.CreditCard.CardOwner.Address.PostalCode = processPaymentRequest.Order.BillingAddress.ZipPostalCode;
            details.CreditCard.CardOwner.Payer = processPaymentRequest.Order.BillingAddress.Email;
            details.CreditCard.CardOwner.PayerName = new PersonNameType();
            details.CreditCard.CardOwner.PayerName.FirstName = processPaymentRequest.Order.BillingAddress.FirstName;
            details.CreditCard.CardOwner.PayerName.LastName = processPaymentRequest.Order.BillingAddress.LastName;

            //order totals
            details.PaymentDetails = new PaymentDetailsType();
            details.PaymentDetails.OrderTotal = new BasicAmountType();
            details.PaymentDetails.OrderTotal.Value = Math.Round(processPaymentRequest.Order.OrderTotal, 2).ToString();
            
            CurrencyCodeType currency;
            Enum.TryParse<CurrencyCodeType>(processPaymentRequest.Order.CustomerCurrency,out currency);
            details.PaymentDetails.OrderTotal.currencyID = currency;

            req.DoDirectPaymentRequest.DoDirectPaymentRequestDetails = details;
            //send request
            using (var service2 = new PayPalAPIAASoapBinding())
            {
                if (GetSetting("use_sandbox") == false)
                    service2.Url = GetSetting("api_url");
                else
                    service2.Url = GetSetting("api_sandboxurl");

                service2.RequesterCredentials = new CustomSecurityHeaderType();
                service2.RequesterCredentials.Credentials = new UserIdPasswordType();
                service2.RequesterCredentials.Credentials.Username = GetSetting("api_username");
                service2.RequesterCredentials.Credentials.Password = GetSetting("api_userpass");
                service2.RequesterCredentials.Credentials.Signature = GetSetting("api_signature");

                DoDirectPaymentResponseType response = service2.DoDirectPayment(req);

                if (response.Ack == AckCodeType.Success || response.Ack == AckCodeType.SuccessWithWarning)
                {
                    result.Success = true;
                }
                else
                {
                    result.Errors = new List<String>();
                    foreach (ErrorType err in response.Errors)
                    {
                        result.Errors.Add(err.LongMessage);
                    }
                }
            }

            PaymentManager.Instance.UpdateCurrentPaymentState(result);
        }

        public CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest)
        {
            var result = new CapturePaymentResult();

            var req = new DoCaptureReq();
            req.DoCaptureRequest = new DoCaptureRequestType();
            req.DoCaptureRequest.Version = "63";
            req.DoCaptureRequest.Amount = new BasicAmountType();
            req.DoCaptureRequest.Amount.Value = Math.Round(capturePaymentRequest.Order.OrderTotal, 2).ToString();
            req.DoCaptureRequest.Amount.currencyID = CurrencyCodeType.USD;
            req.DoCaptureRequest.CompleteType = CompleteCodeType.Complete;

            using (var service2 = new PayPalAPIAASoapBinding())
            {
                if (GetSetting("use_sandbox") == false)
                    service2.Url = GetSetting("api_url");
                else
                    service2.Url = GetSetting("api_sandboxurl");

                service2.RequesterCredentials = new CustomSecurityHeaderType();
                service2.RequesterCredentials.Credentials = new UserIdPasswordType();
                service2.RequesterCredentials.Credentials.Username = GetSetting("api_username");
                service2.RequesterCredentials.Credentials.Password = GetSetting("api_userpass");
                service2.RequesterCredentials.Credentials.Signature = GetSetting("api_signature");

                DoCaptureResponseType response = service2.DoCapture(req);

                if (response.Ack == AckCodeType.Success || response.Ack == AckCodeType.SuccessWithWarning)
                {
                    result.PaymentStatus = PaymentStatus.Paid;
                    result.Success = true;
                }
                else
                {
                    result.Errors = new List<String>();
                    foreach (ErrorType err in response.Errors)
                    {
                        result.Errors.Add(err.LongMessage);
                    }
                }
            }
            return result;
        }

        public RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest)
        {
            var result = new RefundPaymentResult();

            string transactionId = refundPaymentRequest.Order.OrderId;

            var req = new RefundTransactionReq();
            req.RefundTransactionRequest = new RefundTransactionRequestType();
            //NOTE: Specify amount in partial refund
            req.RefundTransactionRequest.RefundType = RefundType.Full;
            req.RefundTransactionRequest.RefundTypeSpecified = true;
            req.RefundTransactionRequest.Version = "63";
            req.RefundTransactionRequest.TransactionID = transactionId;

            using (var service1 = new PayPalAPISoapBinding())
            {
                if (GetSetting("use_sandbox") == false)
                    service1.Url = GetSetting("api_url");
                else
                    service1.Url = GetSetting("api_sandboxurl");

                service1.RequesterCredentials = new CustomSecurityHeaderType();
                service1.RequesterCredentials.Credentials = new UserIdPasswordType();
                service1.RequesterCredentials.Credentials.Username = GetSetting("api_username");
                service1.RequesterCredentials.Credentials.Password = GetSetting("api_userpass");
                service1.RequesterCredentials.Credentials.Signature = GetSetting("api_signature");

                RefundTransactionResponseType response = service1.RefundTransaction(req);

                if (response.Ack == AckCodeType.Success || response.Ack == AckCodeType.SuccessWithWarning)
                {
                    result.PaymentStatus = PaymentStatus.Refunded;
                    result.Success = true;
                }
                else
                {
                    result.Errors = new List<String>();
                    foreach (ErrorType err in response.Errors)
                    {
                        result.Errors.Add(err.LongMessage);
                    }
                }
            }
            return result;
        }

        public VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest)
        {
            var result = new VoidPaymentResult();

            string transactionId = voidPaymentRequest.Order.OrderId;

            var req = new DoVoidReq();
            req.DoVoidRequest = new DoVoidRequestType();
            req.DoVoidRequest.Version = "63";
            req.DoVoidRequest.AuthorizationID = transactionId;

            using (var service2 = new PayPalAPIAASoapBinding())
            {
                if (GetSetting("use_sandbox") == false)
                    service2.Url = GetSetting("api_url");
                else
                    service2.Url = GetSetting("api_sandboxurl");

                service2.RequesterCredentials = new CustomSecurityHeaderType();
                service2.RequesterCredentials.Credentials = new UserIdPasswordType();
                service2.RequesterCredentials.Credentials.Username = GetSetting("api_username");
                service2.RequesterCredentials.Credentials.Password = GetSetting("api_userpass");
                service2.RequesterCredentials.Credentials.Signature = GetSetting("api_signature");

                DoVoidResponseType response = service2.DoVoid(req);

                if (response.Ack == AckCodeType.Success || response.Ack == AckCodeType.SuccessWithWarning)
                {
                    result.PaymentStatus = PaymentStatus.Voided;
                    result.Success = true;
                }
                else
                {
                    result.Errors = new List<String>();
                    foreach (ErrorType err in response.Errors)
                    {
                        result.Errors.Add(err.LongMessage);
                    }
                }
            }
            return result;
        }

        public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "Configure";
            controllerName = "PaypalDirect";
            routeValues = new RouteValueDictionary();
            routeValues.Add("Namespaces", "PaymentHelper.Methods.PaypalDirect.Controllers");
            routeValues.Add("area", null);
        }

        public void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "PaymentInfo";
            controllerName = "PaypalDirect";
            routeValues = new RouteValueDictionary();
            routeValues.Add("Namespaces", "PaymentHelper.Methods.PaypalDirect.Controllers");
            routeValues.Add("area", null);
        }

        public decimal GetHandlingFee()
        {
            return 1.01m;
        }

        public PaymentHelperModel GetConfigModel()
        {
            return null;
        }

        public void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                "PaypalDirect.PaymentInfo",
                "Methods/PaypalDirect/PaymentInfo",
                new { controller = "PaypalDirect", action = "PaymentInfo" },
                new[] { "PaymentHelper.Methods.PaypalDirect.Controllers" }
                );

            routes.MapRoute(
                "PaypalDirect.Configure",
                "Methods/PaypalDirect/PaymentInfo",
                new { controller = "PaypalDirect", action = "PaymentInfo" },
                new[] { "PaymentHelper.Methods.PaypalDirect.Controllers" }
                );
        }
        
        public void GetPaymentInfo(Core.Order orderCurrent, FormCollection collection)
        {
            var creditCard = new Core.CreditCard();
            creditCard.CreditCardCvv2 = collection["CreditCardCVV2"].Trim();
            creditCard.CreditCardExpireMonth = Int32.Parse(collection["ExpMonth"]);
            creditCard.CreditCardExpireYear = Int32.Parse(collection["ExpYear"]);
            creditCard.CreditCardHolderName = collection["CardOwnerName"].Trim();
            creditCard.CreditCardNumber = collection["CreditCardNumber"].Trim();
            creditCard.CreditCardType = collection["CreditCardType"];

            orderCurrent.CreditCard = creditCard;
        }

        public PaymentHelper.Core.MVCUtils.PaymentHelperController GetControllerType()
        {
            return new PaypalDirect.Controllers.PaypalDirectController();
        }

        public bool AcceptCurrency(string currencyCode)
        {
            if (string.IsNullOrEmpty(currencyCode))
                return false;

            string currencySetting = GetSetting("CurrencyAccepted");

            if (string.IsNullOrEmpty(currencySetting))
                return false;

            var listCurrency = currencySetting.SplitToListString(',');
            return listCurrency.Contains(currencyCode);
        }
    }
}
